﻿#ifndef __THREADPOOL_h__
#define __THREADPOOL_h__

#include <deque>
#include <mutex>
#include <functional>
#include <thread>
#include <condition_variable>
#include <string>
#include <future>
#include <memory>
#include <assert.h>

namespace as
{
	namespace thread
	{
		class ThreadPool
		{
		public:
			typedef std::function<void()> TaskFun;

			enum
			{
				DEFAULT_SIZE = 10,
			};

			explicit ThreadPool(const std::string &name = std::string(), int default_queue_size = DEFAULT_SIZE);
			virtual ~ThreadPool();

			ThreadPool &operator=(const ThreadPool &) = delete;
			ThreadPool(const ThreadPool &) = delete;

			void start(int numThreads = std::thread::hardware_concurrency());

			bool CanStop();

			void stop();

			void run(TaskFun t);

			template<class Fun, typename ...Args>
			void enqueue(Fun &&f, Args ...args)
			{
				if (running_ == false)
					return;

#ifdef GAME_DEBUG
				if (task && !!task)
					PrintPushThreadInfo(task);
#endif
				using returnType = std::invoke_result_t<Fun, Args...>;
				auto task = std::make_shared<std::packaged_task<returnType()>>(
					[fun = std::forward<Fun>(f), ...arg = std::forward<Args...>(args)]()
					{
						return fun(arg...);
					}
				);

				if (threads_.empty())
				{
					(*task)();
				}
				else
				{
					std::unique_lock<std::mutex> lock(mutex_);
					while (IsFull())
					{
						notFull_.wait(lock);
					}

					assert(!IsFull());
					queue_.emplace_back([task]() {(*task)(); });
					notEmpty_.notify_one();
				}
			}

			void SetMaxQueueSize(int maxSize) { maxQueueSize_ = maxSize; }

		protected:
			void PrintPushThreadInfo(TaskFun task);
			void PrintRunThreadInfo(TaskFun task);

		private:
			bool IsFull();

			void RunInThread();

			TaskFun take();

		private:
			std::mutex mutex_;
			std::condition_variable notEmpty_;
			std::condition_variable notFull_;
			std::string name_;
			std::vector<std::thread> threads_;
			std::deque<TaskFun> queue_;
			size_t maxQueueSize_;
			bool running_;
		};
	}
}
#endif // __THREADPOOL_h__